use std::collections::HashSet;

use libp2p::{PeerId, core::Multiaddr, gossipsub::MessageId, request_response::ResponseChannel};
use tokio::sync::{mpsc, oneshot};

use crate::BcP2pError;
use crate::ProvidingContentId;
use crate::network_core::P2pNetWorkError;
use crate::network_core::network_core_impl::combined_behaviour::ContentResponse;

// pub enum P2PNetworkCmd {
//     RequestVersion, // if (strCommand == "version") {
//     ResponseVersion(String), // else if (strCommand == "verack") {

//     // BroadcastNodeAddress(Vec<Multiaddr>), // else if (strCommand == "addr")  // mdns, kademlia
//     BroadcastInventory, // else if (strCommand == "inv") {

//     RequestData, // else if (strCommand == "getdata") {
//     RequestBlocks, // else if (strCommand == "getblocks") {

//     BroadcastTx, // else if (strCommand == "tx") {
//     BroadcastBlock, // else if (strCommand == "block") {

//     RequestAddress, // else if (strCommand == "getaddr") {
//     RequestCheckOrder, // else if (strCommand == "checkorder") {
//     RequestSubmitOrder, // else if (strCommand == "submitorder") {
// }
#[derive(Debug)]
pub enum P2PCommandOfApp {
    Broadcast {
        topic: String,
        data: Vec<u8>, // Should be compressed/framed
        cmd_ack_sender: oneshot::Sender<Result<MessageId, BcP2pError>>,
    },

    RequestContent {
        providing_content_id: ProvidingContentId,
        peer: PeerId,
        response_sender: oneshot::Sender<Result<Vec<u8>, P2pNetWorkError>>,
    },

    RespondContent {
        content: Result<Vec<u8>, String>,
        channel: ResponseChannel<ContentResponse>,
    },

    // SendRequest {
    //     peer: PeerId,
    //     data: Vec<u8>,
    //     sender_for_response: oneshot::Sender<Result<Vec<u8>, BcP2pError>>, // error feedback
    // },
    RegisterTopic {
        topic: String,
    },
    StartSync {
        peer: PeerId,
        chunk_ids: Vec<String>,
    },
    // StartListening {
    //     addr: Multiaddr,
    //     cmd_ack_sender: oneshot::Sender<Result<(), P2pNetWorkError>>,
    // },
    Dial {
        peer_id: PeerId,
        peer_addr: Multiaddr,
        cmd_ack_sender: oneshot::Sender<Result<(), P2pNetWorkError>>,
    },

    StartProviding {
        providing_content_id: ProvidingContentId,
        cmd_ack_sender: oneshot::Sender<()>,
    },

    GetProviders {
        providing_content_id: ProvidingContentId,
        cmd_ack_sender: oneshot::Sender<HashSet<PeerId>>,
    },

    GetLocalPeerId {
        cmd_ack_sender: oneshot::Sender<PeerId>,
    },
}

#[derive(Debug)]
pub enum P2PEventForApp {
    /// The new address that is being listened on.
    // address: Multiaddr,
    NewListenAddr(Multiaddr),
    PeerDiscovered(PeerId),
    PeerExpired(PeerId),

    MessageReceived {
        // receive broadcast message
        topic: String,
        from_peer: PeerId,
        data: Vec<u8>,
    },

    // RequestReceived {
    //     peer: PeerId,
    //     data: Vec<u8>,
    //     respond_to: oneshot::Sender<Result<Vec<u8>, BcP2pError>>,
    // },
    InboundRequest {
        requested_content: ProvidingContentId,
        channel: ResponseChannel<ContentResponse>,
    },
    // SyncChunkReceived {
    //     peer: PeerId,
    //     chunk_id: String,
    //     data: Vec<u8>,
    // },
}

#[derive(Clone, Debug)]
pub struct P2pNetWorkHandlerForApp {
    command_tx: mpsc::Sender<P2PCommandOfApp>,
}

impl P2pNetWorkHandlerForApp {
    pub fn new(command_tx: mpsc::Sender<P2PCommandOfApp>) -> Self {
        P2pNetWorkHandlerForApp { command_tx }
    }
}

mod network_api_impl;
